#include "simulation.h"
#include <stack>
SJFSimulation::SJFSimulation(int argc, char **argv) : Simulation(argc, argv) {
}

//schedule a departure event for a given customer
void SJFSimulation::schedule_departure(Customer *customer) {
    for (uint32_t index = 0; index < event_list.size(); ++index) {
        if (event_list[index]->customer_->id_ == customer->id_ &&
            event_list[index]->type_ == 'D') {
            return;
        }
    }
    EventNode *current_departure = new EventNode('D', customer, 
                                                 sim_time + customer->service_time_);
    event_list_add(current_departure);
}

//process an arrival event
void SJFSimulation::process_arrival(EventNode *next_event) {
    //generate the next inter-arrival time
    Ta = expntl(1/lambda, kSeed_arrival);
    if (m == 0) {
        Ts = expntl(1/mu, kSeed_service);
    } else {
        Ts = pareto(&kSeed_service);
    }
    //set the simulation time to the time of the next event
    sim_time = next_event->time_;
    arrival_count++;
    if(customer_queue.empty()) {
        customer_queue.push(next_event->customer_);
    } else {
        uint32_t min_value = customer_queue.size() < numservers ? customer_queue.size() : numservers;
        stack<Customer *> temp_stack;
        while (min_value > 0) {
            Customer *current = customer_queue.front();
            //cout << "removing current = " << current->id_ << " service time = " << current->service_time_ << endl;
            customer_queue.pop();
            temp_stack.push(current);
            min_value--;
        }
        customer_queue.push(next_event->customer_);
        while (! temp_stack.empty()) {
            Customer *current = temp_stack.top();
            customer_queue.push_front(current);
            temp_stack.pop();
        }
    }
    n++;
    if (n <= numservers) {
        for (uint32_t index = 0; index < n; ++index) {
            Customer *current = customer_queue.get(index);
            current->server_id_ = index;
            schedule_departure(current);
        }
    }
    Customer *customer = new Customer(total_arrival_count++, sim_time + Ta, Ts, 0, 0, false, false); 
    customer_list.push_back(customer);
    EventNode *next_arrival = new EventNode('A', customer, sim_time + Ta);
    event_list_add(next_arrival);
}

void SJFSimulation::process_departure(EventNode *next_event) {
    if (! customer_queue.empty()) {
        if (next_event->customer_->id_ != customer_queue.get(next_event->customer_->server_id_)->id_) {
            cout << "caught" << next_event->customer_->id_ << " " 
                 << customer_queue.front()->id_ << endl;
        }
        Customer *departing_customer = next_event->customer_;
        sim_time = next_event->time_;
        departing_customer->departure_time_ = sim_time;
        departing_customer->serviced_ = true;
        departing_customer->lost_ = false;
        departing_customer->departure_number_ = ++departure_count;
        customer_queue.remove(departing_customer->server_id_);
        departing_customer->customers_after_ = customer_queue.size();
        n--;
        serve_count++;
        if (n > 0) {
            uint32_t min_value = n < numservers ? n : numservers;
            for (uint32_t index = 0; index < min_value; ++index) {
                Customer *current = customer_queue.get(index);
                current->server_id_ = index;
                schedule_departure(current);
            }
        }
    } 
}

void SJFSimulation::print_queues() {
    for (uint32_t index = 0; index < customer_queue.size(); ++index) {
        cout << customer_queue.get(index)->id_ << " ";
    }
    cout << endl;
}

//PriorityQueue methods


//Push in order of increasing service times
void SJFSimulation::PriorityQueue::push(Customer *customer) {
    if (customer_queue_.empty()) {
        customer_queue_.push_back(customer);
        return;
    } else if (customer_queue_.size() == 1) {
        if (customer_queue_[0]->service_time_ <= customer->service_time_) {
            customer_queue_.push_back(customer);
        } else {
            customer_queue_.insert(customer_queue_.begin(), customer);
        } 
    } else {
        uint32_t count = 0;
        while (customer_queue_[count]->service_time_ <= customer->service_time_) { 
            count++;
            if (count == customer_queue_.size()) {
                break;
            }
        }
        //cout << "count = " << count << endl;
        if (count < customer_queue_.size()) {
            customer_queue_.insert(customer_queue_.begin() + count, customer);
        } else {
            customer_queue_.push_back(customer);
        }
    }
}

//Push to the front of the queue
void SJFSimulation::PriorityQueue::push_front(Customer *customer) {
    customer_queue_.push_front(customer);
}

//Pop the front element from the queue
void SJFSimulation::PriorityQueue::pop() {
    customer_queue_.pop_front();
}

void SJFSimulation::PriorityQueue::remove(uint32_t index) {
    customer_queue_.erase(customer_queue_.begin() + index);
}

//Get the front element in the queue
Simulation::Customer *SJFSimulation::PriorityQueue::front() {
    return customer_queue_.front();
}

//Get the size of the queue
uint32_t SJFSimulation::PriorityQueue::size() {
    return customer_queue_.size();
}

//Check whether the queue is empty
bool SJFSimulation::PriorityQueue::empty() {
    return customer_queue_.empty();
}

Simulation::Customer *SJFSimulation::PriorityQueue::get(uint32_t index) {
    return customer_queue_[index];
}
